#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
用户界面模块
"""

import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import threading
import time
import os
from datetime import datetime
from modules.audio_player import AudioPlayer
from modules.scheduler import BellScheduler
from modules.database import DatabaseManager
from config import AUDIO_DIR

class BellSystemUI:
    def __init__(self):
        """初始化用户界面"""
        self.root = tk.Tk()
        self.root.title("铃声系统")
        self.root.geometry("800x700")
        
        # 初始化模块
        self.audio_player = AudioPlayer()
        self.scheduler = BellScheduler()
        self.db_manager = DatabaseManager()
        
        # 设置铃声触发回调函数
        self.scheduler.set_bell_trigger_callback(self._play_scheduled_bell)
        
        # 界面变量
        self.is_scheduler_running = False
        # 星期方案映射变量
        self.day_plan_vars = {}
        self.day_plan_combos = {}
        
        # 创建界面
        self.create_widgets()
        
        # 加载已保存的星期方案设置
        self.load_saved_weekday_plans()
        
        # 立即执行已保存的星期方案设置
        self.execute_saved_weekday_plans()
        
        # 自动启动系统
        self.auto_start_system()
        
        # 更新时间显示
        self.update_time_display()
    
    def auto_start_system(self):
        """自动启动系统"""
        try:
            self.scheduler.start_scheduler()
            self.is_scheduler_running = True
            self.start_button.config(state=tk.DISABLED)
            self.stop_button.config(state=tk.NORMAL)
            self.skip_bell_button.config(state=tk.NORMAL)
            self.log_message("系统已自动启动")
        except Exception as e:
            messagebox.showerror("错误", f"自动启动系统时出错: {e}")
    
    def create_widgets(self):
        """创建界面控件"""
        # 创建标签页
        self.notebook = ttk.Notebook(self.root)
        self.notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 主控制页面
        self.create_main_tab()
        
        # 时间表管理页面
        self.create_schedule_tab()
        
        # 铃声库管理页面
        self.create_audio_tab()
        
        # 系统设置页面
        self.create_settings_tab()
    
    def create_main_tab(self):
        """创建主控制页面"""
        self.main_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.main_frame, text="主控制台")
        
        # 当前时间显示
        time_frame = ttk.LabelFrame(self.main_frame, text="当前时间")
        time_frame.pack(fill=tk.X, padx=10, pady=5)
        
        self.time_label = ttk.Label(time_frame, text="", font=("Arial", 24))
        self.time_label.pack(pady=10)
        
        # 下次铃声显示
        next_bell_frame = ttk.LabelFrame(self.main_frame, text="下次铃声")
        next_bell_frame.pack(fill=tk.X, padx=10, pady=5)
        
        self.next_bell_label = ttk.Label(next_bell_frame, text="暂无", font=("Arial", 16))
        self.next_bell_label.pack(pady=10)
        
        # 控制按钮
        control_frame = ttk.Frame(self.main_frame)
        control_frame.pack(fill=tk.X, padx=10, pady=20)
        
        self.start_button = ttk.Button(control_frame, text="启动系统", command=self.start_system)
        self.start_button.pack(side=tk.LEFT, padx=5)
        
        self.stop_button = ttk.Button(control_frame, text="停止系统", command=self.stop_system, state=tk.DISABLED)
        self.stop_button.pack(side=tk.LEFT, padx=5)
        
        self.test_bell_button = ttk.Button(control_frame, text="测试铃声", command=self.test_bell)
        self.test_bell_button.pack(side=tk.LEFT, padx=5)
        
        # 添加停止播放铃声按钮
        self.stop_play_button = ttk.Button(control_frame, text="停止播放", command=self.stop_bell_play, state=tk.NORMAL)
        self.stop_play_button.pack(side=tk.LEFT, padx=5)
        
        self.skip_bell_button = ttk.Button(control_frame, text="跳过下次铃声", command=self.skip_next_bell, state=tk.DISABLED)
        self.skip_bell_button.pack(side=tk.LEFT, padx=5)
        
        # 星期方案设置（去掉系统状态标签框架）
        week_frame = ttk.LabelFrame(self.main_frame, text="星期方案设置")
        week_frame.pack(fill=tk.X, padx=10, pady=5)
        
        # 星期名称映射
        weekdays = {
            'monday': '周一',
            'tuesday': '周二',
            'wednesday': '周三',
            'thursday': '周四',
            'friday': '周五',
            'saturday': '周六',
            'sunday': '周日'
        }
        
        # 获取所有可用方案
        plans = self.db_manager.get_active_plans()
        plan_options = ["无"] + [plan[1] for plan in plans]  # 使用方案名称
        
        # 为每个星期几创建行
        for i, (day_key, day_name) in enumerate(weekdays.items()):
            day_frame = ttk.Frame(week_frame)
            day_frame.pack(fill=tk.X, padx=5, pady=2)
            
            # 星期标签
            ttk.Label(day_frame, text=day_name, width=10).pack(side=tk.LEFT)
            
            # 方案选择下拉列表
            self.day_plan_vars[day_key] = tk.StringVar()
            combo = ttk.Combobox(day_frame, textvariable=self.day_plan_vars[day_key], 
                               values=["无"] + plan_options, state="readonly", width=30)
            combo.pack(side=tk.LEFT, padx=5)
            combo.set("无")  # 默认选择"无"
            self.day_plan_combos[day_key] = combo
            
            # 刷新按钮
            ttk.Button(day_frame, text="刷新", command=self.refresh_day_plan_options, width=8).pack(side=tk.LEFT, padx=5)
        
        # 执行按钮
        execute_frame = ttk.Frame(week_frame)
        execute_frame.pack(fill=tk.X, padx=5, pady=10)
        
        ttk.Button(execute_frame, text="保存并执行", command=self.save_and_execute_day_plans, width=15).pack()
        
        # 日志显示区域
        log_frame = ttk.LabelFrame(self.main_frame, text="运行日志")
        log_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
        
        self.status_text = tk.Text(log_frame, height=15)
        self.status_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        scrollbar = ttk.Scrollbar(self.status_text, command=self.status_text.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.status_text.config(yscrollcommand=scrollbar.set)
        
        # 加载已保存的星期方案设置
        self.load_saved_weekday_plans()
    
    def load_saved_weekday_plans(self):
        """加载已保存的星期方案设置"""
        try:
            # 获取所有星期方案设置
            weekday_plans = self.db_manager.get_all_weekday_plans()
            
            # 获取所有可用方案
            plans = self.db_manager.get_active_plans()
            plan_id_to_name = {plan[0]: plan[1] for plan in plans}
            
            # 设置界面中的方案选择
            for day_key, plan_id in weekday_plans.items():
                if day_key in self.day_plan_vars:
                    if plan_id and plan_id in plan_id_to_name:
                        plan_name = plan_id_to_name[plan_id]
                        self.day_plan_vars[day_key].set(plan_name)
                    else:
                        self.day_plan_vars[day_key].set("无")
        except Exception as e:
            self.log_message(f"加载星期方案设置时出错: {e}")
    
    def execute_saved_weekday_plans(self):
        """执行已保存的星期方案设置"""
        try:
            # 更新调度器中的当前星期方案
            self.update_scheduler_for_current_weekday()
            
            self.log_message("已执行已保存的星期方案设置")
        except Exception as e:
            self.log_message(f"执行已保存的星期方案设置时出错: {e}")
    
    def refresh_day_plan_options(self):
        """刷新星期方案选项"""
        # 获取所有可用方案
        plans = self.db_manager.get_active_plans()
        plan_options = ["无"] + [plan[1] for plan in plans]  # 使用方案名称
        
        # 更新所有下拉列表的选项
        for combo in self.day_plan_combos.values():
            combo['values'] = plan_options
    
    def execute_day_plans(self):
        """执行星期方案设置"""
        # 收集所有星期几的方案设置
        day_plans = {}
        for day_key, var in self.day_plan_vars.items():
            plan_name = var.get()
            if plan_name != "无":
                day_plans[day_key] = plan_name
            else:
                day_plans[day_key] = None
        
        # 记录执行日志
        self.log_message("执行星期方案设置:")
        for day_key, plan_name in day_plans.items():
            if plan_name:
                self.log_message(f"  {self._get_chinese_weekday(day_key)}: {plan_name}")
            else:
                self.log_message(f"  {self._get_chinese_weekday(day_key)}: 无方案")
        
        # 这里可以添加实际的方案执行逻辑
        # 例如：将方案与具体的日期关联，或者更新调度器等
        self.log_message("方案设置已应用")
    
    def save_and_execute_day_plans(self):
        """保存并执行星期方案设置"""
        try:
            # 收集所有星期几的方案设置
            day_plans = {}
            for day_key, var in self.day_plan_vars.items():
                plan_name = var.get()
                if plan_name != "无":
                    day_plans[day_key] = plan_name
                else:
                    day_plans[day_key] = None
            
            # 保存到数据库
            success_count = 0
            for day_key, plan_name in day_plans.items():
                if plan_name:
                    # 通过方案名称查找方案ID
                    plans = self.db_manager.get_active_plans()
                    plan_id = None
                    for plan in plans:
                        if plan[1] == plan_name:
                            plan_id = plan[0]
                            break
                    
                    if plan_id:
                        # 保存星期方案设置
                        if self.db_manager.save_weekday_plan(day_key, plan_id):
                            success_count += 1
                        else:
                            self.log_message(f"保存{self._get_chinese_weekday(day_key)}方案失败")
                    else:
                        self.log_message(f"未找到方案'{plan_name}'的ID")
                else:
                    # 保存空值（无方案）
                    if self.db_manager.save_weekday_plan(day_key, None):
                        success_count += 1
                    else:
                        self.log_message(f"保存{self._get_chinese_weekday(day_key)}方案失败")
            
            # 记录执行日志
            self.log_message("保存并执行星期方案设置:")
            for day_key, plan_name in day_plans.items():
                if plan_name:
                    self.log_message(f"  {self._get_chinese_weekday(day_key)}: {plan_name}")
                else:
                    self.log_message(f"  {self._get_chinese_weekday(day_key)}: 无方案")
            
            self.log_message(f"方案设置已保存并应用 (成功保存 {success_count}/{len(day_plans)} 个设置)")
            
            # 更新调度器中的当前星期方案
            self.update_scheduler_for_current_weekday()
            
        except Exception as e:
            messagebox.showerror("错误", f"保存并执行星期方案设置时出错: {e}")
            self.log_message(f"保存并执行星期方案设置时出错: {e}")
    
    def update_scheduler_for_current_weekday(self):
        """根据当前星期几更新调度器的方案"""
        try:
            # 获取当前星期几
            current_weekday = datetime.now().strftime("%A").lower()
            
            # 获取该星期几对应的方案ID
            weekday_plans = self.db_manager.get_all_weekday_plans()
            plan_id = weekday_plans.get(current_weekday, None)
            
            # 设置调度器的当前方案
            self.scheduler.set_weekday_plan(plan_id)
            
            # 更新下次铃声显示
            self.update_next_bell_display()
            
            self.log_message(f"已更新调度器方案为 {current_weekday} ({plan_id if plan_id else '无方案'})")
        except Exception as e:
            self.log_message(f"更新调度器方案时出错: {e}")
    
    def _get_chinese_weekday(self, day_key):
        """获取星期的中文名称"""
        weekdays = {
            'monday': '周一',
            'tuesday': '周二',
            'wednesday': '周三',
            'thursday': '周四',
            'friday': '周五',
            'saturday': '周六',
            'sunday': '周日'
        }
        return weekdays.get(day_key, day_key)
    
    def create_schedule_tab(self):
        """创建方案管理页面"""
        self.schedule_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.schedule_frame, text="方案管理")
        
        # 时间表列表
        list_frame = ttk.Frame(self.schedule_frame)
        list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
        
        self.schedule_tree = ttk.Treeview(list_frame, columns=("序号", "名称", "类型", "状态"), show="headings")
        self.schedule_tree.heading("序号", text="序号")
        self.schedule_tree.heading("名称", text="名称")
        self.schedule_tree.heading("类型", text="类型")
        self.schedule_tree.heading("状态", text="状态")
        self.schedule_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.schedule_tree.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.schedule_tree.config(yscrollcommand=scrollbar.set)
        
        # 操作按钮
        button_frame = ttk.Frame(self.schedule_frame)
        button_frame.pack(fill=tk.X, padx=10, pady=5)
        
        ttk.Button(button_frame, text="刷新", command=self.refresh_plans).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="添加", command=self.add_plan).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="编辑", command=self.edit_plan).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="删除", command=self.delete_plan).pack(side=tk.LEFT, padx=5)
        
        # 加载方案数据
        self.refresh_plans()
    
    def create_audio_tab(self):
        """创建铃声库管理页面"""
        self.audio_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.audio_frame, text="铃声库管理")
        
        # 音频文件列表
        list_frame = ttk.Frame(self.audio_frame)
        list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
        
        self.audio_tree = ttk.Treeview(list_frame, columns=("序号", "文件名", "路径", "时长", "上传时间"), show="headings")
        self.audio_tree.heading("序号", text="序号")
        self.audio_tree.heading("文件名", text="文件名")
        self.audio_tree.heading("路径", text="路径")
        self.audio_tree.heading("时长", text="时长")
        self.audio_tree.heading("上传时间", text="上传时间")
        self.audio_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.audio_tree.yview)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.audio_tree.config(yscrollcommand=scrollbar.set)
        
        # 操作按钮
        button_frame = ttk.Frame(self.audio_frame)
        button_frame.pack(fill=tk.X, padx=10, pady=5)
        
        ttk.Button(button_frame, text="刷新", command=self.refresh_audio_files).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="上传", command=self.upload_audio).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="删除", command=self.delete_audio).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="试听", command=self.preview_audio).pack(side=tk.LEFT, padx=5)
        
        # 加载音频文件数据
        self.refresh_audio_files()
    
    def create_settings_tab(self):
        """创建系统设置页面"""
        self.settings_frame = ttk.Frame(self.notebook)
        self.notebook.add(self.settings_frame, text="系统设置")
        
        # 音量设置
        volume_frame = ttk.LabelFrame(self.settings_frame, text="音量设置")
        volume_frame.pack(fill=tk.X, padx=10, pady=5)
        
        ttk.Label(volume_frame, text="默认音量:").pack(side=tk.LEFT, padx=5)
        self.volume_var = tk.IntVar(value=80)
        self.volume_scale = ttk.Scale(volume_frame, from_=0, to=100, orient=tk.HORIZONTAL, variable=self.volume_var)
        self.volume_scale.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
        self.volume_label = ttk.Label(volume_frame, text="80%")
        self.volume_label.pack(side=tk.LEFT, padx=5)
        
        # 绑定音量滑块事件
        self.volume_scale.config(command=self.on_volume_change)
        
        # 其他设置
        other_frame = ttk.LabelFrame(self.settings_frame, text="其他设置")
        other_frame.pack(fill=tk.X, padx=10, pady=5)
        
        ttk.Button(other_frame, text="保存设置", command=self.save_settings).pack(padx=5, pady=5)
    
    def update_time_display(self):
        """更新时间显示"""
        now = datetime.now()
        # 获取星期几的中文名称
        weekdays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
        weekday_name = weekdays[now.weekday()]
        # 格式化时间显示，包含日期、星期和时间
        current_time = now.strftime(f"%Y-%m-%d {weekday_name} %H:%M:%S")
        self.time_label.config(text=current_time)
        
        # 更新下次铃声显示
        self.update_next_bell_display()
        
        # 每秒更新一次
        self.root.after(1000, self.update_time_display)
    
    def update_next_bell_display(self):
        """更新下次铃声显示"""
        next_bell = self.scheduler.get_next_bell_time()
        if next_bell:
            # 提取铃声文件名（去掉路径）
            if isinstance(next_bell, dict):
                time_str = next_bell['time']
                audio_file = next_bell['audio_file']
                # 获取文件名部分
                audio_name = os.path.basename(audio_file)
                self.next_bell_label.config(text=f"{time_str} - {audio_name}")
            else:
                self.next_bell_label.config(text=str(next_bell))
        else:
            self.next_bell_label.config(text="暂无")
    
    def start_system(self):
        """启动系统"""
        try:
            self.scheduler.start_scheduler()
            self.is_scheduler_running = True
            self.start_button.config(state=tk.DISABLED)
            self.stop_button.config(state=tk.NORMAL)
            self.skip_bell_button.config(state=tk.NORMAL)
            self.log_message("系统已启动")
        except Exception as e:
            messagebox.showerror("错误", f"启动系统时出错: {e}")
    
    def stop_system(self):
        """停止系统"""
        try:
            self.scheduler.stop_scheduler()
            self.is_scheduler_running = False
            self.start_button.config(state=tk.NORMAL)
            self.stop_button.config(state=tk.DISABLED)
            self.skip_bell_button.config(state=tk.DISABLED)
            self.log_message("系统已停止")
        except Exception as e:
            messagebox.showerror("错误", f"停止系统时出错: {e}")
    
    def test_bell(self):
        """测试铃声"""
        # 检查是否正在播放
        if hasattr(self, 'is_test_bell_playing') and self.is_test_bell_playing:
            # 停止播放
            self.audio_player.stop_audio()
            self.is_test_bell_playing = False
            self.test_bell_button.config(text="测试铃声")
            self.log_message("测试铃声已停止")
            return
        
        # 获取下次铃声信息
        next_bell = self.scheduler.get_next_bell_time()
        
        if next_bell and isinstance(next_bell, dict):
            # 获取音频文件路径
            audio_file = next_bell['audio_file']
            
            # 记录调试信息
            self.log_message(f"测试铃声: 尝试播放文件 {audio_file}")
            
            # 检查文件路径是否为绝对路径
            if not os.path.isabs(audio_file):
                # 如果是相对路径，尝试在音频目录中查找
                from config import AUDIO_DIR
                audio_file = os.path.join(AUDIO_DIR, os.path.basename(audio_file))
                self.log_message(f"测试铃声: 转换为绝对路径 {audio_file}")
            
            # 检查文件是否存在
            if not os.path.exists(audio_file):
                # 尝试在音频目录中查找文件
                from config import AUDIO_DIR
                alternative_path = os.path.join(AUDIO_DIR, os.path.basename(audio_file))
                if os.path.exists(alternative_path):
                    audio_file = alternative_path
                    self.log_message(f"测试铃声: 在音频目录找到文件 {audio_file}")
                else:
                    messagebox.showerror("错误", f"音频文件不存在: {audio_file}\n已尝试查找路径: {alternative_path}")
                    self.log_message(f"测试铃声失败: 音频文件不存在 {audio_file}")
                    return
            
            # 设置播放状态
            self.is_test_bell_playing = True
            self.test_bell_button.config(text="停止播放")
            
            # 在新线程中播放音频，避免阻塞UI
            def play_test_bell():
                try:
                    self.log_message(f"开始测试播放下次铃声: {os.path.basename(audio_file)}")
                    success = self.audio_player.play_audio(audio_file)
                    if success:
                        self.log_message(f"测试铃声播放完成: {os.path.basename(audio_file)}")
                    else:
                        self.log_message(f"测试铃声播放失败: {os.path.basename(audio_file)}")
                except Exception as e:
                    self.log_message(f"测试铃声播放出错: {e}")
                finally:
                    # 播放完成后重置按钮状态
                    self.is_test_bell_playing = False
                    self.test_bell_button.config(text="测试铃声")
            
            # 启动播放线程
            audio_thread = threading.Thread(target=play_test_bell)
            audio_thread.daemon = True
            audio_thread.start()
        else:
            messagebox.showinfo("提示", "当前没有待播放的铃声")
            self.log_message("测试铃声: 当前没有待播放的铃声")
    
    def stop_bell_play(self):
        """停止播放铃声"""
        self.audio_player.stop_audio()
        self.log_message("铃声播放已停止")
    
    def skip_next_bell(self):
        """跳过下次铃声"""
        skipped_bell = self.scheduler.skip_next_bell()
        if skipped_bell:
            # 处理字典类型的返回值
            if isinstance(skipped_bell, dict):
                time_str = skipped_bell['time']
                audio_file = skipped_bell['audio_file']
                audio_name = os.path.basename(audio_file)
                display_text = f"{time_str} - {audio_name}"
            else:
                display_text = str(skipped_bell)
            
            self.log_message(f"已跳过下次铃声: {display_text}")
            # 更新下次铃声显示
            self.update_next_bell_display()
        else:
            self.log_message("没有待播放的铃声")
    
    def refresh_plans(self):
        """刷新方案列表"""
        # 清空现有数据
        for item in self.schedule_tree.get_children():
            self.schedule_tree.delete(item)
        
        # 加载新数据
        plans = self.db_manager.get_active_plans()
        for index, plan in enumerate(plans, start=1):
            status = "启用" if True else "禁用"  # 简化处理
            self.schedule_tree.insert("", tk.END, values=(index, plan[1], plan[2], status))
        
        # 同时刷新星期方案选项
        self.refresh_day_plan_options()
    
    def refresh_plans_with_renumber(self):
        """刷新方案列表并重新编号"""
        self.refresh_plans()
    
    def add_plan(self):
        """添加方案"""
        # 创建新方案对话框
        self.plan_dialog = tk.Toplevel(self.root)
        self.plan_dialog.title("新建方案")
        self.plan_dialog.geometry("600x500")
        self.plan_dialog.transient(self.root)
        self.plan_dialog.grab_set()
        
        # 方案名称
        name_frame = ttk.Frame(self.plan_dialog)
        name_frame.pack(fill=tk.X, padx=10, pady=5)
        
        ttk.Label(name_frame, text="方案名称:").pack(side=tk.LEFT)
        self.plan_name_var = tk.StringVar()
        ttk.Entry(name_frame, textvariable=self.plan_name_var, width=30).pack(side=tk.LEFT, padx=5)
        
        # 铃声时间点列表
        bell_frame = ttk.LabelFrame(self.plan_dialog, text="铃声时间点")
        bell_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
        
        # 铃声时间点列表
        list_frame = ttk.Frame(bell_frame)
        list_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        self.bell_tree = ttk.Treeview(list_frame, columns=("时间", "铃声类型", "音频文件", "音量"), show="headings")
        self.bell_tree.heading("时间", text="时间")
        self.bell_tree.heading("铃声类型", text="铃声类型")
        self.bell_tree.heading("音频文件", text="音频文件")
        self.bell_tree.heading("音量", text="音量")
        self.bell_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        bell_scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.bell_tree.yview)
        bell_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.bell_tree.config(yscrollcommand=bell_scrollbar.set)
        
        # 操作按钮
        bell_button_frame = ttk.Frame(bell_frame)
        bell_button_frame.pack(fill=tk.X, padx=5, pady=5)
        
        ttk.Button(bell_button_frame, text="添加", command=self.add_bell_time).pack(side=tk.LEFT, padx=5)
        ttk.Button(bell_button_frame, text="编辑", command=self.edit_bell_time).pack(side=tk.LEFT, padx=5)
        ttk.Button(bell_button_frame, text="删除", command=self.delete_bell_time).pack(side=tk.LEFT, padx=5)
        
        # 保存按钮
        button_frame = ttk.Frame(self.plan_dialog)
        button_frame.pack(fill=tk.X, padx=10, pady=10)
        
        ttk.Button(button_frame, text="保存", command=self.save_plan).pack(side=tk.RIGHT, padx=5)
        ttk.Button(button_frame, text="取消", command=self.plan_dialog.destroy).pack(side=tk.RIGHT, padx=5)
        
        # 居中显示对话框
        self.plan_dialog.update_idletasks()
        x = (self.plan_dialog.winfo_screenwidth() // 2) - (self.plan_dialog.winfo_width() // 2)
        y = (self.plan_dialog.winfo_screenheight() // 2) - (self.plan_dialog.winfo_height() // 2)
        self.plan_dialog.geometry(f"+{x}+{y}")
    
    def _find_plan_id_by_name(self, plan_name):
        """通过方案名称查找方案ID
        
        Args:
            plan_name (str): 方案名称
            
        Returns:
            int: 方案ID，如果未找到返回None
        """
        try:
            plans = self.db_manager.get_active_plans()
            for plan in plans:
                if plan[1] == plan_name:
                    return plan[0]
            return None
        except Exception as e:
            print(f"查找方案ID时出错: {e}")
            return None
    
    def edit_plan(self):
        """编辑方案"""
        # 获取选中的方案
        selected = self.schedule_tree.selection()
        if not selected:
            messagebox.showwarning("警告", "请先选择一个方案")
            return
        
        # 获取选中项的值
        item = self.schedule_tree.item(selected[0])
        values = item['values']
        # 由于现在显示的是序号而不是ID，我们需要通过名称查找真实的ID
        plan_name = values[1]
        
        # 通过名称查找方案ID
        plan_id = self._find_plan_id_by_name(plan_name)
        
        if plan_id is None:
            messagebox.showerror("错误", "未找到选中的方案")
            return
        
        # 创建编辑方案对话框
        self.plan_dialog = tk.Toplevel(self.root)
        self.plan_dialog.title(f"编辑方案 - {plan_name}")
        self.plan_dialog.geometry("600x500")
        self.plan_dialog.transient(self.root)
        self.plan_dialog.grab_set()
        
        # 存储方案ID
        self.current_plan_id = plan_id
        
        # 方案名称
        name_frame = ttk.Frame(self.plan_dialog)
        name_frame.pack(fill=tk.X, padx=10, pady=5)
        
        ttk.Label(name_frame, text="方案名称:").pack(side=tk.LEFT)
        self.plan_name_var = tk.StringVar(value=plan_name)
        ttk.Entry(name_frame, textvariable=self.plan_name_var, width=30).pack(side=tk.LEFT, padx=5)
        
        # 铃声时间点列表
        bell_frame = ttk.LabelFrame(self.plan_dialog, text="铃声时间点")
        bell_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)
        
        # 铃声时间点列表
        list_frame = ttk.Frame(bell_frame)
        list_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        self.bell_tree = ttk.Treeview(list_frame, columns=("时间", "铃声类型", "音频文件", "音量"), show="headings")
        self.bell_tree.heading("时间", text="时间")
        self.bell_tree.heading("铃声类型", text="铃声类型")
        self.bell_tree.heading("音频文件", text="音频文件")
        self.bell_tree.heading("音量", text="音量")
        self.bell_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        bell_scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.bell_tree.yview)
        bell_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        self.bell_tree.config(yscrollcommand=bell_scrollbar.set)
        
        # 操作按钮
        bell_button_frame = ttk.Frame(bell_frame)
        bell_button_frame.pack(fill=tk.X, padx=5, pady=5)
        
        ttk.Button(bell_button_frame, text="添加", command=self.add_bell_time).pack(side=tk.LEFT, padx=5)
        ttk.Button(bell_button_frame, text="编辑", command=self.edit_bell_time).pack(side=tk.LEFT, padx=5)
        ttk.Button(bell_button_frame, text="删除", command=self.delete_bell_time).pack(side=tk.LEFT, padx=5)
        
        # 保存按钮
        button_frame = ttk.Frame(self.plan_dialog)
        button_frame.pack(fill=tk.X, padx=10, pady=10)
        
        ttk.Button(button_frame, text="保存", command=self.save_plan).pack(side=tk.RIGHT, padx=5)
        ttk.Button(button_frame, text="取消", command=self.plan_dialog.destroy).pack(side=tk.RIGHT, padx=5)
        
        # 居中显示对话框
        self.plan_dialog.update_idletasks()
        x = (self.plan_dialog.winfo_screenwidth() // 2) - (self.plan_dialog.winfo_width() // 2)
        y = (self.plan_dialog.winfo_screenheight() // 2) - (self.plan_dialog.winfo_height() // 2)
        self.plan_dialog.geometry(f"+{x}+{y}")
        
        # 加载该方案的铃声时间点
        self._load_bell_times_for_plan(plan_id)
    
    def _load_bell_times_for_plan(self, plan_id):
        """加载方案的铃声时间点"""
        try:
            bell_times = self.db_manager.get_bell_times_by_plan(plan_id)
            for bell_time in bell_times:
                time_str, bell_type, audio_file, volume = bell_time
                volume_str = f"{volume}%"
                self.bell_tree.insert("", tk.END, values=(time_str, bell_type, audio_file, volume_str))
        except Exception as e:
            messagebox.showerror("错误", f"加载铃声时间点时出错: {e}")
    
    def delete_plan(self):
        """删除方案"""
        # 获取选中的方案
        selected = self.schedule_tree.selection()
        if not selected:
            messagebox.showwarning("警告", "请先选择一个方案")
            return
        
        # 获取选中项的值
        item = self.schedule_tree.item(selected[0])
        values = item['values']
        # 由于现在显示的是序号而不是ID，我们需要通过名称查找真实的ID
        plan_name = values[1]
        
        # 通过名称查找方案ID
        plan_id = self._find_plan_id_by_name(plan_name)
        
        if plan_id is None:
            messagebox.showerror("错误", "未找到选中的方案")
            return
        
        # 确认删除
        if not messagebox.askyesno("确认删除", f"确定要删除方案 '{plan_name}' 吗？\n注意：此操作将同时删除该方案下的所有铃声时间点。"):
            return
        
        try:
            # 从数据库中删除方案（包括相关的铃声时间点）
            success = self.db_manager.delete_plan(plan_id)
            
            if success:
                # 从界面中移除
                self.schedule_tree.delete(selected[0])
                self.log_message(f"方案 '{plan_name}' 已删除")
                
                # 刷新星期方案选项
                self.refresh_day_plan_options()
                
                # 重新刷新方案列表以更新序号
                self.refresh_plans_with_renumber()
            else:
                messagebox.showerror("错误", "删除方案失败")
        except Exception as e:
            messagebox.showerror("错误", f"删除方案时出错: {e}")
            self.log_message(f"删除方案时出错: {e}")
    
    def refresh_audio_files(self):
        """刷新音频文件列表"""
        # 清空现有数据
        for item in self.audio_tree.get_children():
            self.audio_tree.delete(item)
        
        # 加载新数据
        audio_files = self.db_manager.get_all_audio_files()
        for index, audio in enumerate(audio_files, start=1):
            duration = f"{audio[3]}秒" if audio[3] > 0 else "未知"
            self.audio_tree.insert("", tk.END, values=(index, audio[1], audio[2], duration, audio[4]))
    
    def refresh_audio_files_with_renumber(self):
        """刷新音频文件列表并重新编号"""
        self.refresh_audio_files()
    
    def upload_audio(self):
        """上传音频文件"""
        file_path = filedialog.askopenfilename(
            title="选择音频文件",
            filetypes=[("音频文件", "*.mp3 *.wav *.ogg"), ("所有文件", "*.*")]
        )
        
        if file_path:
            try:
                # 验证文件
                if not self._validate_audio_file(file_path):
                    return
                
                # 处理文件上传
                result = self._process_audio_upload(file_path)
                if result:
                    self.log_message(f"音频文件上传成功: {result['filename']}")
                    # 刷新音频文件列表
                    self.refresh_audio_files()
                else:
                    self.log_message("音频文件上传失败")
            except Exception as e:
                messagebox.showerror("错误", f"上传音频文件时出错: {e}")
                self.log_message(f"上传音频文件时出错: {e}")
    
    def _find_audio_id_by_name(self, audio_name):
        """通过音频文件名查找音频ID
        
        Args:
            audio_name (str): 音频文件名
            
        Returns:
            int: 音频ID，如果未找到返回None
        """
        try:
            audio_files = self.db_manager.get_all_audio_files()
            for audio in audio_files:
                if audio[1] == audio_name:
                    return audio[0]
            return None
        except Exception as e:
            print(f"查找音频ID时出错: {e}")
            return None
    
    def delete_audio(self):
        """删除音频文件"""
        # 获取选中的音频文件
        selected = self.audio_tree.selection()
        if not selected:
            messagebox.showwarning("警告", "请先选择一个音频文件")
            return
        
        # 获取选中项的值
        item = self.audio_tree.item(selected[0])
        values = item['values']
        audio_name = values[1]  # 文件名
        
        # 通过名称查找音频文件ID
        audio_id = self._find_audio_id_by_name(audio_name)
        
        if audio_id is None:
            messagebox.showerror("错误", "未找到选中的音频文件")
            return
        
        # 确认删除
        if not messagebox.askyesno("确认删除", f"确定要删除音频文件 '{audio_name}' 吗？"):
            return
        
        try:
            # 从数据库中删除记录
            success = self.db_manager.delete_audio_file(audio_id)
            
            if success:
                # 删除文件系统中的文件
                file_path = values[2]  # 文件路径
                if os.path.exists(file_path):
                    try:
                        os.remove(file_path)
                    except Exception as e:
                        self.log_message(f"删除文件时出错: {e}")
                
                # 从界面中移除
                self.audio_tree.delete(selected[0])
                self.log_message(f"音频文件 '{audio_name}' 已删除")
                
                # 刷新铃声下拉列表（如果在新建方案界面中打开）
                self._refresh_audio_combo_if_needed()
                
                # 重新刷新音频文件列表以更新序号
                self.refresh_audio_files_with_renumber()
            else:
                messagebox.showerror("错误", "删除音频文件失败")
        except Exception as e:
            messagebox.showerror("错误", f"删除音频文件时出错: {e}")
            self.log_message(f"删除音频文件时出错: {e}")
    
    def _refresh_audio_combo_if_needed(self):
        """如果在新建方案界面中打开，刷新铃声下拉列表"""
        # 检查是否在新建方案界面中打开
        if hasattr(self, 'plan_dialog') and self.plan_dialog and self.plan_dialog.winfo_exists():
            # 刷新铃声下拉列表
            self._refresh_audio_combo()
    
    def add_bell_time(self):
        """添加铃声时间点"""
        # 创建添加铃声时间点对话框
        self.bell_dialog = tk.Toplevel(self.plan_dialog)
        self.bell_dialog.title("添加铃声时间点")
        self.bell_dialog.geometry("450x250")
        self.bell_dialog.transient(self.plan_dialog)
        self.bell_dialog.grab_set()
        
        # 时间设置（精确到秒）
        time_frame = ttk.Frame(self.bell_dialog)
        time_frame.pack(fill=tk.X, padx=10, pady=5)
        
        ttk.Label(time_frame, text="时间:").pack(side=tk.LEFT)
        
        # 小时下拉列表
        self.hour_var = tk.StringVar(value="08")
        hours = [f"{i:02d}" for i in range(24)]
        self.hour_combo = ttk.Combobox(time_frame, textvariable=self.hour_var, values=hours, width=5, state="readonly")
        self.hour_combo.pack(side=tk.LEFT, padx=2)
        
        ttk.Label(time_frame, text=":").pack(side=tk.LEFT)
        
        # 分钟下拉列表
        self.minute_var = tk.StringVar(value="00")
        minutes = [f"{i:02d}" for i in range(60)]
        self.minute_combo = ttk.Combobox(time_frame, textvariable=self.minute_var, values=minutes, width=5, state="readonly")
        self.minute_combo.pack(side=tk.LEFT, padx=2)
        
        ttk.Label(time_frame, text=":").pack(side=tk.LEFT)
        
        # 秒下拉列表
        self.second_var = tk.StringVar(value="00")
        seconds = [f"{i:02d}" for i in range(60)]
        self.second_combo = ttk.Combobox(time_frame, textvariable=self.second_var, values=seconds, width=5, state="readonly")
        self.second_combo.pack(side=tk.LEFT, padx=2)
        
        # 铃声选择
        audio_frame = ttk.Frame(self.bell_dialog)
        audio_frame.pack(fill=tk.X, padx=10, pady=5)
        
        ttk.Label(audio_frame, text="铃声:").pack(side=tk.LEFT)
        self.audio_file_var = tk.StringVar()
        # 获取铃声库中的音频文件
        audio_files = self._get_audio_files_for_combo()
        self.audio_combo = ttk.Combobox(audio_frame, textvariable=self.audio_file_var, values=audio_files, width=30, state="readonly")
        self.audio_combo.pack(side=tk.LEFT, padx=5)
        
        # 刷新铃声列表按钮
        ttk.Button(audio_frame, text="刷新", command=self._refresh_audio_combo).pack(side=tk.LEFT, padx=5)
        
        # 按钮
        button_frame = ttk.Frame(self.bell_dialog)
        button_frame.pack(fill=tk.X, padx=10, pady=10)
        
        ttk.Button(button_frame, text="确定", command=self.confirm_bell_time).pack(side=tk.RIGHT, padx=5)
        ttk.Button(button_frame, text="取消", command=self.bell_dialog.destroy).pack(side=tk.RIGHT, padx=5)
        
        # 居中显示对话框
        self.bell_dialog.update_idletasks()
        x = (self.bell_dialog.winfo_screenwidth() // 2) - (self.bell_dialog.winfo_width() // 2)
        y = (self.bell_dialog.winfo_screenheight() // 2) - (self.bell_dialog.winfo_height() // 2)
        self.bell_dialog.geometry(f"+{x}+{y}")
    
    def edit_bell_time(self):
        """编辑铃声时间点"""
        selected = self.bell_tree.selection()
        if not selected:
            messagebox.showwarning("警告", "请先选择一个铃声时间点")
            return
        
        # 获取选中项的值
        item = self.bell_tree.item(selected[0])
        values = item['values']
        
        # 创建编辑铃声时间点对话框
        self.bell_dialog = tk.Toplevel(self.plan_dialog)
        self.bell_dialog.title("编辑铃声时间点")
        self.bell_dialog.geometry("450x250")
        self.bell_dialog.transient(self.plan_dialog)
        self.bell_dialog.grab_set()
        
        # 时间设置（精确到秒）
        time_frame = ttk.Frame(self.bell_dialog)
        time_frame.pack(fill=tk.X, padx=10, pady=5)
        
        ttk.Label(time_frame, text="时间:").pack(side=tk.LEFT)
        
        # 时间格式为 HH:MM:SS
        time_parts = values[0].split(":")
        hour = time_parts[0] if len(time_parts) > 0 else "08"
        minute = time_parts[1] if len(time_parts) > 1 else "00"
        second = time_parts[2] if len(time_parts) > 2 else "00"
        
        # 小时下拉列表
        self.hour_var = tk.StringVar(value=hour)
        hours = [f"{i:02d}" for i in range(24)]
        self.hour_combo = ttk.Combobox(time_frame, textvariable=self.hour_var, values=hours, width=5, state="readonly")
        self.hour_combo.pack(side=tk.LEFT, padx=2)
        
        ttk.Label(time_frame, text=":").pack(side=tk.LEFT)
        
        # 分钟下拉列表
        self.minute_var = tk.StringVar(value=minute)
        minutes = [f"{i:02d}" for i in range(60)]
        self.minute_combo = ttk.Combobox(time_frame, textvariable=self.minute_var, values=minutes, width=5, state="readonly")
        self.minute_combo.pack(side=tk.LEFT, padx=2)
        
        ttk.Label(time_frame, text=":").pack(side=tk.LEFT)
        
        # 秒下拉列表
        self.second_var = tk.StringVar(value=second)
        seconds = [f"{i:02d}" for i in range(60)]
        self.second_combo = ttk.Combobox(time_frame, textvariable=self.second_var, values=seconds, width=5, state="readonly")
        self.second_combo.pack(side=tk.LEFT, padx=2)
        
        # 铃声选择
        audio_frame = ttk.Frame(self.bell_dialog)
        audio_frame.pack(fill=tk.X, padx=10, pady=5)
        
        ttk.Label(audio_frame, text="铃声:").pack(side=tk.LEFT)
        self.audio_file_var = tk.StringVar(value=values[2])
        # 获取铃声库中的音频文件
        audio_files = self._get_audio_files_for_combo()
        self.audio_combo = ttk.Combobox(audio_frame, textvariable=self.audio_file_var, values=audio_files, width=30, state="readonly")
        self.audio_combo.pack(side=tk.LEFT, padx=5)
        
        # 刷新铃声列表按钮
        ttk.Button(audio_frame, text="刷新", command=self._refresh_audio_combo).pack(side=tk.LEFT, padx=5)
        
        # 按钮
        button_frame = ttk.Frame(self.bell_dialog)
        button_frame.pack(fill=tk.X, padx=10, pady=10)
        
        ttk.Button(button_frame, text="确定", command=lambda: self.update_bell_time(selected[0])).pack(side=tk.RIGHT, padx=5)
        ttk.Button(button_frame, text="取消", command=self.bell_dialog.destroy).pack(side=tk.RIGHT, padx=5)
        
        # 居中显示对话框
        self.bell_dialog.update_idletasks()
        x = (self.bell_dialog.winfo_screenwidth() // 2) - (self.bell_dialog.winfo_width() // 2)
        y = (self.bell_dialog.winfo_screenheight() // 2) - (self.bell_dialog.winfo_height() // 2)
        self.bell_dialog.geometry(f"+{x}+{y}")
    
    def delete_bell_time(self):
        """删除铃声时间点"""
        selected = self.bell_tree.selection()
        if not selected:
            messagebox.showwarning("警告", "请先选择一个铃声时间点")
            return
        
        # 确认删除
        if messagebox.askyesno("确认", "确定要删除选中的铃声时间点吗？"):
            self.bell_tree.delete(selected[0])
    
    def update_bell_time(self, item_id):
        """更新铃声时间点"""
        # 获取时间（精确到秒）
        time_str = f"{self.hour_var.get()}:{self.minute_var.get()}:{self.second_var.get()}"
        
        # 获取音频文件
        audio_file = self.audio_file_var.get()
        
        # 更新铃声时间点列表（铃声类型和音量设为默认值）
        self.bell_tree.item(item_id, values=(time_str, "铃声", audio_file, "80%"))
        
        # 关闭对话框
        self.bell_dialog.destroy()
    
    def save_plan(self):
        """保存方案"""
        try:
            # 获取方案名称
            plan_name = self.plan_name_var.get().strip()
            if not plan_name:
                messagebox.showwarning("警告", "请输入方案名称")
                return
            
            # 获取铃声时间点
            bell_times = []
            for child in self.bell_tree.get_children():
                values = self.bell_tree.item(child)['values']
                bell_times.append({
                    'time': values[0],
                    'type': values[1],
                    'audio_file': values[2],
                    'volume': int(values[3].replace('%', ''))
                })
            
            # 数据验证
            if not self._validate_plan_data(plan_name, bell_times):
                return
            
            # 检查是新建还是编辑
            if hasattr(self, 'current_plan_id'):
                # 编辑现有方案
                plan_id = self.current_plan_id
                success = self._update_plan_in_database(plan_id, plan_name, bell_times)
            else:
                # 新建方案
                plan_id = self._save_plan_to_database(plan_name)
                if not isinstance(plan_id, int) or plan_id <= 0:
                    messagebox.showerror("错误", "保存方案失败")
                    return
                success = self._save_bell_times_to_database(plan_id, bell_times)
                # 如果是新建方案且保存铃声时间点失败，回滚方案
                if not success:
                    self._delete_plan_from_database(plan_id)
            
            if not success:
                messagebox.showerror("错误", "保存方案失败")
                return
            
            # 保存成功
            self.log_message(f"方案 '{plan_name}' 已保存，铃声时间点数: {len(bell_times)}")
            messagebox.showinfo("成功", f"方案 '{plan_name}' 保存成功")
            
            # 刷新方案列表
            self.refresh_plans()
            
            # 关闭对话框
            self.plan_dialog.destroy()
            
        except Exception as e:
            messagebox.showerror("错误", f"保存方案时出错: {e}")
            self.log_message(f"保存方案时出错: {e}")
    
    def _update_plan_in_database(self, plan_id, plan_name, bell_times):
        """更新方案到数据库"""
        try:
            # 更新方案基本信息
            plan_type = "custom"
            update_success = self.db_manager.update_plan(plan_id, plan_name, plan_type)
            if not update_success:
                return False
            
            # 删除原有的铃声时间点
            delete_success = self.db_manager.delete_bell_times_by_plan(plan_id)
            if not delete_success:
                return False
            
            # 保存新的铃声时间点
            save_success = self._save_bell_times_to_database(plan_id, bell_times)
            if not save_success:
                # 如果保存失败，尝试恢复原有数据
                self.log_message(f"保存铃声时间点失败，方案 '{plan_name}' 更新已回滚")
                return False
            
            return True
        except Exception as e:
            print(f"更新方案到数据库时出错: {e}")
            # 记录错误日志
            self.log_message(f"更新方案 '{plan_name}' 时出错: {e}")
            return False
    
    def browse_audio_file(self):
        """浏览音频文件"""
        file_path = filedialog.askopenfilename(
            parent=self.bell_dialog,
            title="选择音频文件",
            filetypes=[("音频文件", "*.mp3 *.wav *.ogg"), ("所有文件", "*.*")]
        )
        
        if file_path:
            self.audio_file_var.set(file_path)
    
    def _get_audio_files_for_combo(self):
        """获取铃声库中的音频文件列表用于下拉框
        
        Returns:
            list: 音频文件名列表
        """
        try:
            audio_files = self.db_manager.get_all_audio_files()
            # 返回文件名列表
            return [audio[1] for audio in audio_files] if audio_files else ["无可用铃声"]
        except Exception as e:
            print(f"获取音频文件列表时出错: {e}")
            return ["无可用铃声"]
    
    def _refresh_audio_combo(self):
        """刷新铃声下拉列表"""
        audio_files = self._get_audio_files_for_combo()
        self.audio_combo['values'] = audio_files
        if audio_files and audio_files[0] != "无可用铃声":
            self.audio_file_var.set(audio_files[0])
        else:
            self.audio_file_var.set("")
    
    def _validate_plan_data(self, plan_name, bell_times):
        """验证方案数据
        
        Args:
            plan_name (str): 方案名称
            bell_times (list): 铃声时间点列表
            
        Returns:
            bool: 验证是否通过
        """
        # 验证方案名称长度
        if len(plan_name) > 50:
            messagebox.showerror("错误", "方案名称不能超过50个字符")
            return False
        
        # 验证铃声时间点
        for bell_time in bell_times:
            # 验证时间格式
            time_parts = bell_time['time'].split(':')
            if len(time_parts) != 3:
                messagebox.showerror("错误", "时间格式不正确")
                return False
            
            try:
                hour = int(time_parts[0])
                minute = int(time_parts[1])
                second = int(time_parts[2])
                
                if not (0 <= hour <= 23 and 0 <= minute <= 59 and 0 <= second <= 59):
                    messagebox.showerror("错误", "时间值超出有效范围")
                    return False
            except ValueError:
                messagebox.showerror("错误", "时间格式不正确")
                return False
            
            # 验证音频文件路径
            if not bell_time['audio_file']:
                messagebox.showerror("错误", "请选择音频文件")
                return False
            
            # 验证音量范围
            if not (0 <= bell_time['volume'] <= 100):
                messagebox.showerror("错误", "音量值应在0-100之间")
                return False
        
        return True
    
    def _save_plan_to_database(self, plan_name):
        """保存方案到数据库
        
        Args:
            plan_name (str): 方案名称
            
        Returns:
            int: 方案ID，失败返回-1
        """
        try:
            # 默认类型为custom
            plan_type = "custom"
            
            # 保存方案
            plan_id = self.db_manager.add_plan(plan_name, plan_type)
            return plan_id
        except Exception as e:
            print(f"保存方案到数据库时出错: {e}")
            return -1
    
    def _save_bell_times_to_database(self, plan_id, bell_times):
        """保存铃声时间点到数据库
        
        Args:
            plan_id (int): 方案ID
            bell_times (list): 铃声时间点列表
            
        Returns:
            bool: 是否保存成功
        """
        try:
            for bell_time in bell_times:
                result = self.db_manager.add_bell_time(
                    plan_id,
                    bell_time['time'],
                    bell_time['type'],
                    bell_time['audio_file'],
                    bell_time['volume']
                )
                if result <= 0:
                    return False
            return True
        except Exception as e:
            print(f"保存铃声时间点到数据库时出错: {e}")
            return False
    
    def _delete_plan_from_database(self, plan_id):
        """从数据库删除方案（回滚操作）
        
        Args:
            plan_id (int): 方案ID
        """
        try:
            self.db_manager.delete_plan(plan_id)
        except Exception as e:
            print(f"回滚方案时出错: {e}")
    
    def confirm_bell_time(self):
        """确认铃声时间点"""
        # 获取时间（精确到秒）
        time_str = f"{self.hour_var.get()}:{self.minute_var.get()}:{self.second_var.get()}"
        
        # 获取音频文件
        audio_file = self.audio_file_var.get()
        
        # 添加到铃声时间点列表（铃声类型和音量设为默认值）
        self.bell_tree.insert("", tk.END, values=(time_str, "铃声", audio_file, "80%"))
        
        # 关闭对话框
        self.bell_dialog.destroy()
    
    def preview_audio(self):
        """试听音频"""
        # 获取选中的音频文件
        selected = self.audio_tree.selection()
        if not selected:
            messagebox.showwarning("警告", "请先选择一个音频文件")
            return
        
        # 获取选中项的值
        item = self.audio_tree.item(selected[0])
        values = item['values']
        
        # 获取音频文件路径
        file_path = values[2]  # 第3列是文件路径
        
        # 检查文件是否存在
        if not os.path.exists(file_path):
            messagebox.showerror("错误", f"音频文件不存在: {file_path}")
            return
        
        # 创建试听对话框
        self.preview_dialog = tk.Toplevel(self.root)
        self.preview_dialog.title(f"试听音频 - {values[1]}")
        self.preview_dialog.geometry("400x200")
        self.preview_dialog.transient(self.root)
        self.preview_dialog.grab_set()
        
        # 文件信息
        info_frame = ttk.Frame(self.preview_dialog)
        info_frame.pack(fill=tk.X, padx=10, pady=10)
        
        ttk.Label(info_frame, text=f"文件名: {values[1]}").pack(anchor=tk.W)
        ttk.Label(info_frame, text=f"时长: {values[3]}").pack(anchor=tk.W)
        
        # 播放控制
        control_frame = ttk.Frame(self.preview_dialog)
        control_frame.pack(fill=tk.X, padx=10, pady=10)
        
        self.play_button = ttk.Button(control_frame, text="播放", command=lambda: self._play_preview_audio(file_path))
        self.play_button.pack(side=tk.LEFT, padx=5)
        
        self.stop_button = ttk.Button(control_frame, text="停止", command=self._stop_preview_audio, state=tk.DISABLED)
        self.stop_button.pack(side=tk.LEFT, padx=5)
        
        # 音量控制
        volume_frame = ttk.Frame(self.preview_dialog)
        volume_frame.pack(fill=tk.X, padx=10, pady=10)
        
        ttk.Label(volume_frame, text="音量:").pack(side=tk.LEFT)
        self.preview_volume_var = tk.IntVar(value=self.audio_player.get_volume())
        volume_scale = ttk.Scale(volume_frame, from_=0, to=100, orient=tk.HORIZONTAL, 
                                variable=self.preview_volume_var, command=self._on_preview_volume_change)
        volume_scale.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
        self.preview_volume_label = ttk.Label(volume_frame, text=f"{self.audio_player.get_volume()}%")
        self.preview_volume_label.pack(side=tk.LEFT)
        
        # 关闭按钮
        close_frame = ttk.Frame(self.preview_dialog)
        close_frame.pack(fill=tk.X, padx=10, pady=10)
        
        ttk.Button(close_frame, text="关闭", command=self.preview_dialog.destroy).pack(side=tk.RIGHT)
        
        # 居中显示对话框
        self.preview_dialog.update_idletasks()
        x = (self.preview_dialog.winfo_screenwidth() // 2) - (self.preview_dialog.winfo_width() // 2)
        y = (self.preview_dialog.winfo_screenheight() // 2) - (self.preview_dialog.winfo_height() // 2)
        self.preview_dialog.geometry(f"+{x}+{y}")
        
        # 记录当前播放状态
        self.is_preview_playing = False
    
    def _validate_audio_file(self, file_path):
        """验证音频文件
        
        Args:
            file_path (str): 文件路径
            
        Returns:
            bool: 验证是否通过
        """
        import os
        
        # 检查文件是否存在
        if not os.path.exists(file_path):
            messagebox.showerror("错误", "选择的文件不存在")
            return False
        
        # 检查文件大小（限制为100MB）
        file_size = os.path.getsize(file_path)
        if file_size > 100 * 1024 * 1024:  # 100MB
            messagebox.showerror("错误", "音频文件大小不能超过100MB")
            return False
        
        # 检查文件扩展名
        valid_extensions = ['.mp3', '.wav', '.ogg']
        _, ext = os.path.splitext(file_path.lower())
        if ext not in valid_extensions:
            messagebox.showerror("错误", f"不支持的音频格式，仅支持: {', '.join(valid_extensions)}")
            return False
        
        return True
    
    def _process_audio_upload(self, file_path):
        """处理音频文件上传
        
        Args:
            file_path (str): 源文件路径
            
        Returns:
            dict: 上传结果信息
        """
        import os
        import shutil
        from config import AUDIO_DIR
        
        target_path = None
        
        try:
            # 获取文件名
            filename = os.path.basename(file_path)
            
            # 构建目标路径
            target_path = os.path.join(AUDIO_DIR, filename)
            
            # 如果文件已存在，添加序号
            counter = 1
            original_filename, ext = os.path.splitext(filename)
            while os.path.exists(target_path):
                new_filename = f"{original_filename}_{counter}{ext}"
                target_path = os.path.join(AUDIO_DIR, new_filename)
                counter += 1
            
            # 复制文件到音频目录
            shutil.copy2(file_path, target_path)
            
            # 获取文件信息
            file_size = os.path.getsize(target_path)
            # 简化处理，实际应用中可以使用音频处理库获取准确时长
            duration = self._estimate_audio_duration(file_size)
            
            # 保存到数据库
            audio_id = self.db_manager.add_audio_file(
                os.path.basename(target_path),
                target_path,
                duration
            )
            
            if audio_id > 0:
                return {
                    'id': audio_id,
                    'filename': os.path.basename(target_path),
                    'path': target_path,
                    'duration': duration
                }
            else:
                # 如果数据库保存失败，删除已复制的文件
                if os.path.exists(target_path):
                    os.remove(target_path)
                return None
                
        except Exception as e:
            # 如果出现错误，删除可能已复制的文件
            if target_path and os.path.exists(target_path):
                os.remove(target_path)
            raise e
    
    def _estimate_audio_duration(self, file_size):
        """估算音频时长（简化处理）
        
        Args:
            file_size (int): 文件大小（字节）
            
        Returns:
            int: 估算的时长（秒）
        """
        # 简化的估算方法，实际应用中应使用音频处理库
        # 假设平均比特率为128kbps
        bitrate = 128 * 1024  # 128 kbps = 128 * 1024 bps
        duration_seconds = (file_size * 8) // bitrate
        return max(1, duration_seconds)  # 至少1秒
    
    def on_volume_change(self, value):
        """音量改变事件"""
        volume = int(float(value))
        self.volume_label.config(text=f"{volume}%")
        self.audio_player.set_volume(volume)
    
    def save_settings(self):
        """保存设置"""
        self.log_message("设置已保存")
    
    def log_message(self, message):
        """记录日志消息"""
        timestamp = datetime.now().strftime("%H:%M:%S")
        self.status_text.insert(tk.END, f"[{timestamp}] {message}\n")
        self.status_text.see(tk.END)
    
    def _play_scheduled_bell(self, audio_file, volume):
        """
        播放计划铃声
        
        Args:
            audio_file (str): 音频文件路径
            volume (int): 音量大小
        """
        try:
            self.log_message(f"播放计划铃声: {audio_file} (音量: {volume})")
            
            # 处理不同的路径格式
            if not os.path.isabs(audio_file):
                # 如果是相对路径，尝试在音频目录中查找
                # 处理不同格式的路径
                filename = os.path.basename(audio_file)
                audio_file = os.path.join(AUDIO_DIR, filename)
            
            # 检查文件是否存在
            if not os.path.exists(audio_file):
                # 尝试其他可能的路径格式
                alternative_paths = [
                    os.path.basename(audio_file),  # 仅文件名
                    os.path.join(AUDIO_DIR, os.path.basename(audio_file)),  # 标准路径
                ]
                
                found = False
                for path in alternative_paths:
                    if os.path.exists(path):
                        audio_file = path
                        found = True
                        break
                
                if not found:
                    self.log_message(f"铃声文件不存在: {audio_file}")
                    return
            
            # 在新线程中播放音频，避免阻塞调度器
            def play_scheduled_audio():
                try:
                    # 设置音量
                    self.audio_player.set_volume(volume)
                    
                    # 播放音频
                    success = self.audio_player.play_audio(audio_file)
                    if success:
                        self.log_message(f"计划铃声播放完成: {os.path.basename(audio_file)}")
                    else:
                        self.log_message(f"计划铃声播放失败: {os.path.basename(audio_file)}")
                except Exception as e:
                    self.log_message(f"计划铃声播放出错: {e}")
            
            # 启动播放线程
            audio_thread = threading.Thread(target=play_scheduled_audio)
            audio_thread.daemon = True
            audio_thread.start()
            
        except Exception as e:
            self.log_message(f"播放计划铃声时出错: {e}")
    
    def _play_preview_audio(self, file_path):
        """播放预览音频
        
        Args:
            file_path (str): 音频文件路径
        """
        try:
            # 设置音量
            volume = self.preview_volume_var.get()
            self.audio_player.set_volume(volume)
            
            # 在新线程中播放音频，避免阻塞UI
            def play_audio_thread():
                self.is_preview_playing = True
                self.play_button.config(state=tk.DISABLED)
                self.stop_button.config(state=tk.NORMAL)
                
                # 播放音频
                success = self.audio_player.play_audio(file_path)
                
                # 播放完成后更新UI
                self.is_preview_playing = False
                self.play_button.config(state=tk.NORMAL)
                self.stop_button.config(state=tk.DISABLED)
                
                if not success:
                    messagebox.showerror("错误", f"播放音频失败: {file_path}")
            
            # 启动播放线程
            audio_thread = threading.Thread(target=play_audio_thread)
            audio_thread.daemon = True
            audio_thread.start()
            
        except Exception as e:
            messagebox.showerror("错误", f"播放音频时出错: {e}")
            self.is_preview_playing = False
            self.play_button.config(state=tk.NORMAL)
            self.stop_button.config(state=tk.DISABLED)
    
    def _stop_preview_audio(self):
        """停止预览音频播放"""
        self.audio_player.stop_audio()
        self.is_preview_playing = False
        self.play_button.config(state=tk.NORMAL)
        self.stop_button.config(state=tk.DISABLED)
    
    def _on_preview_volume_change(self, value):
        """预览音量改变事件
        
        Args:
            value (str): 音量值
        """
        volume = int(float(value))
        self.preview_volume_label.config(text=f"{volume}%")
        # 如果正在播放，实时调整音量
        if self.is_preview_playing:
            self.audio_player.set_volume(volume)
    
    def run(self):
        """运行界面"""
        self.root.mainloop()

def main():
    # 创建并运行用户界面
    app = BellSystemUI()
    app.run()

if __name__ == "__main__":
    main()